﻿#include <xy/topo/shape.h>

namespace xy
{

namespace topo
{

bool shape_t::has_property(const property_t &prop) const
{
    auto it = std::find_if(m_properties.begin(), m_properties.end(),
                           [&](const auto &val) { return val->name() == prop.name(); });
    return it != m_properties.end();
}

void shape_t::remove_property(const property_t &prop)
{
    auto it = std::find_if(m_properties.begin(), m_properties.end(),
                           [&](const auto &val) { return val->name() == prop.name(); });
    m_properties.erase(it);
}

bool shape_t::remove(shape_t *shape)
{
    // 注意要在源文件里引用对应拓扑的头文件，否则报错（艹！！！）
    if (shape == nullptr)
        return false;
    delete shape;
    return true;
}

vertex_t *shape_t::start_vertex(coedge_t *co)
{
    if (co == nullptr)
        return nullptr;
    else if (co->dir == Topo_Dir::Forward)
        return co->edge->start;
    else
        return co->edge->end;
}

vertex_t *shape_t::end_vertex(coedge_t *co)
{
    if (co == nullptr)
        return nullptr;
    else if (co->dir == Topo_Dir::Forward)
        return co->edge->end;
    else
        return co->edge->start;
}

std::vector<coedge_t *> shape_t::coedges_on_face(face_t *f)
{
    if (f == nullptr)
        return {};

    // 取第一个 loop 上的所有顶点
    auto loop = f->wires.front();
    auto co = loop->coedge;
    std::vector<coedge_t *> coList{co};

    while (co->next != loop->coedge)
    {
        co = co->next;
        coList.push_back(co);
    }
    return coList;
}

coedge_t *shape_t::common_coedge_t(vertex_t *v0, vertex_t *v1)
{
    if (v0 == nullptr || v1 == nullptr)
        return nullptr;

    // 首先获得连接 v 的边
    const auto &eList = v0->edges;
    for (const auto &e : eList)
    {
        if (e->start == v0 && e->end == v1)
            return e->coedge;
        if (e->start == v1 && e->end == v0)
            return e->coedge->twin;
    }
    return nullptr;
}

std::vector<edge_t *> shape_t::common_edges(face_t *f0, face_t *f1)
{
    if (f0 == nullptr || f1 == nullptr)
        return {};

    std::vector<edge_t *> eList;
    auto loop0 = f0->wires.front();
    auto loop1 = f1->wires.front();
    auto co0 = loop0->coedge;
    auto co1 = loop1->coedge;

    do
    {
        do
        {
            // 循环记录公共边
            if (co0->edge == co1->edge)
            {
                eList.push_back(co0->edge);
                break;
            }

            co1 = co1->next;
        } while (co1 != loop1->coedge);
        co0 = co0->next;
        co1 = loop1->coedge;
    } while (co0 != loop0->coedge);

    return eList;
}

std::vector<coedge_t *> shape_t::adjacent_coedges(vertex_t *v)
{
    if (v == nullptr)
        return {};

    // 首先获得连接 v 的边
    std::vector<coedge_t *> coList;
    const auto &eList = v->edges;
    for (const auto &e : eList)
    {
        if (e->start == v)
            coList.push_back(e->coedge);
        else
            coList.push_back(e->coedge->twin);
    }
    return coList;
}

std::unordered_set<edge_t *> shape_t::adjacent_edges(edge_t *e)
{
    if (e == nullptr)
        return {};

    std::unordered_set<edge_t *> eList;
    const auto &eList0 = e->start->edges;
    const auto &eList1 = e->end->edges;

    for (const auto &e0 : eList0)
        eList.insert(e0);
    for (const auto &e1 : eList1)
        eList.insert(e1);
    eList.erase(e);

    return eList;
}

std::unordered_set<face_t *> shape_t::adjacent_faces(vertex_t *v)
{
    if (v == nullptr)
        return {};

    // 使用 set 避免重复元素
    std::unordered_set<face_t *> fList;

    // 首先获得连接 v 的边
    const auto &eList = v->edges;
    for (const auto &e : eList)
    {
        if (e->coedge->wire != nullptr && e->coedge->wire->face != nullptr)
            fList.insert(e->coedge->wire->face);
        if (e->coedge->twin->wire != nullptr && e->coedge->twin->wire->face != nullptr)
            fList.insert(e->coedge->twin->wire->face);
    }
    return fList;
}

std::unordered_set<face_t *> shape_t::adjacent_faces(face_t *f)
{
    if (f == nullptr)
        return {};

    // 使用 set 避免重复元素
    std::unordered_set<face_t *> fList;

    // 首先获得 f 的所有边
    for (auto co : coedges_on_face(f))
    {
        if (co->wire != nullptr && co->wire->face != nullptr)
            fList.insert(co->wire->face);
        if (co->twin->wire != nullptr && co->twin->wire->face != nullptr)
            fList.insert(co->twin->wire->face);
    }
    fList.erase(f);

    return fList;
}

std::deque<edge_t *> shape_t::adjacent_ordered_edges(coedge_t *co)
{
    if (co == nullptr)
        return {};

    std::deque<edge_t *> eList;

    // 找到第一个边的起点
    // 先逆时针寻找
    auto e = co;
    while (e != nullptr)
    {
        eList.push_back(e->edge);
        e = e->prev;

        // 如果回到初始边，就退出
        if (e == nullptr || e->twin == co)
            break;

        e = e->twin;
    }

    // 如果没有回到初始边，就顺时针寻找
    if (e == nullptr)
    {
        e = co->twin->next;
        while (e != nullptr)
        {
            eList.push_front(e->edge);
            e = e->twin->next;
        }
        // 这里不需要判断回到初始边，因为如果一个方向不能返回，那么反过来也不能返回
    }

    return eList;
}

std::vector<coedge_t *> shape_t::edges(wire_t *wire)
{
    if (wire == nullptr || wire->coedge == nullptr)
        return {};

    auto co = wire->coedge;
    std::vector<coedge_t *> eList;
    do
    {
        eList.push_back(co);
        co = co->next;
    } while (co != wire->coedge);
    return eList;
}

vertex_t::~vertex_t()
{
    // 删除所有边，注意先移除边数组，因为删 edge_t 时会修改边数组
    std::vector<edge_t *> tedges = std::move(edges);

    // 现在可以删除所有边
    for (auto e : tedges)
        shape_t::remove(e);
}

vertex_t *vertex_t::create()
{
    return new vertex_t;
}

edge_t::~edge_t()
{
    // 解除连接，删除 coedge_t（coedge_t 会删除 twin）
    if (coedge != nullptr)
    {
        coedge->edge = nullptr;
        coedge->twin->edge = nullptr;
        shape_t::remove(coedge);
        coedge = nullptr;
    }

    // 断开顶点连接
    std::erase(start->edges, this);
    std::erase(end->edges, this);
}

edge_t *edge_t::create(vertex_t *v1, vertex_t *v2)
{
    // 禁止创建自环边
    if (v1 == nullptr || v2 == nullptr)
        throw std::logic_error("v1 and v2 should not be null");
    if (v1 == v2)
        throw std::logic_error("v1 and v2 should not be the same");

    // 创建新的边
    edge_t *e = new edge_t;

    e->start = v1;
    e->end = v2;

    // 创建 coedge_t
    auto co1 = coedge_t::create(Topo_Dir::Forward);
    auto co2 = coedge_t::create(Topo_Dir::Backward);

    // 建立拓扑连接
    e->coedge = co1;
    co1->edge = e;
    co2->edge = e;
    co1->twin = co2;
    co2->twin = co1;

    // 设置顶点的 edge_t
    v1->edges.push_back(e);
    v2->edges.push_back(e);

    return e;
}

coedge_t::coedge_t(Topo_Dir dir) : dir(dir)
{
}

coedge_t::~coedge_t()
{
    // 移除 twin
    if (twin != nullptr)
    {
        twin->twin = nullptr;
        twin->edge = nullptr;
        shape_t::remove(twin);
        twin = nullptr;
    }

    // 移除 edge
    if (edge != nullptr)
    {
        edge->coedge = nullptr;
        shape_t::remove(edge);
        edge = nullptr;
    }

    // 移除 wire
    if (wire != nullptr)
    {
        // 这里不能将 wire->coedge 置空，因为需要 wire 遍历 coedge_t 断开连接
        shape_t::remove(wire);
        wire = nullptr;
    }
    else
    {
        // 断开前后连接（如果 wire 存在，则 wire 负责断开）
        if (next != nullptr)
            next->prev = nullptr;
        if (prev != nullptr)
            prev->next = nullptr;

        next = nullptr;
        prev = nullptr;
    }
}

coedge_t *coedge_t::create(Topo_Dir dir)
{
    return new coedge_t{dir};
}

bool coedge_t::is_boundary() const
{
    // 如果此半边不属于任何 wire，或者所属 wire 是边界，则一定在边界上
    return wire == nullptr || wire->loop == Topo_Loop::Boundary;
}

wire_t::~wire_t()
{
    // 断开所有 coedge_t 连接
    auto co = coedge;
    auto co2 = co;
    while (co != nullptr)
    {
        co = co->next;
        co2->wire = nullptr;
        co2->prev = nullptr;
        co2->next = nullptr;
        co2 = co;

        if (co == coedge)
            break;
    }

    if (face != nullptr)
    {
        // 从 face_t 中移除 wire
        std::erase(face->wires, this);

        // 如果 wire 是 face_t 上的唯一 wire，应该删除 face_t
        if (face->wires.empty())
            shape_t::remove(face);
    }
}

wire_t *wire_t::create(const std::vector<coedge_t *> &es)
{
    if (es.empty())
        throw std::logic_error("es should not be empty");

    for (auto e : es)
        if (e == nullptr)
            throw std::logic_error("e should not be null");

    // 检测是否有重复的边
    auto checkDuplicate = [&]() -> bool {
        std::set<coedge_t *> eset;
        for (auto e : es)
            eset.insert(e);
        return eset.size() != es.size();
    };

    if (checkDuplicate())
        throw std::logic_error("es should not contain duplicate elements");

    // 直接创建 loop，不需要创建边
    auto loop = new wire_t;
    loop->coedge = es.front();

    // 获得一个起点
    auto e0 = es.front();
    e0->prev = es.back();
    es.back()->next = e0;
    e0->wire = loop;

    if (coedge_t::start_vertex(e0) != coedge_t::end_vertex(e0->prev))
        throw std::logic_error("coedge_t should be connected");

    // 需要建立半边之间的联系
    for (std::size_t i = 1; i < es.size(); i++)
    {
        es[i]->prev = es[i - 1];
        es[i - 1]->next = es[i];
        es[i]->wire = loop;
        if (coedge_t::start_vertex(es[i]) != coedge_t::end_vertex(es[i - 1]))
            throw std::logic_error("coedge_t should be connected");
    }

    return loop;
}

face_t::~face_t()
{
    // 断开所有 wire 连接
    for (auto wire : wires)
        wire->face = nullptr;

    // 只删除面上的 wire_t，不删除边界上的 wire_t
    for (std::size_t i = 1; i < wires.size(); i++)
        shape_t::remove(wires[i]);
}

face_t *face_t::create(const std::vector<coedge_t *> &es)
{
    auto loop = wire_t::create(es);
    loop->face = new face_t;
    loop->face->wires.emplace_back(loop);
    return loop->face;
}

} // namespace topo

} // namespace xy